package com.example.demo13_persistentrememberme.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

import javax.sql.DataSource;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private DataSource dataSource;
    // 明文加密方式
    @Bean
    PasswordEncoder passwordEncoder(){
        return NoOpPasswordEncoder.getInstance();
    }

    // 向外暴露 UserDetailsService
    @Override
    @Bean
    public UserDetailsService userDetailsServiceBean() throws Exception {
        return super.userDetailsServiceBean();
    }
    // 基于内存用户
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().withUser("wangnian").password("123").roles("admin");
    }

    // 配置 rememberMe 的令牌持久化仓库，如果想要自定义仓库，自定义类实现 PersistentTokenRepository 并现实其方法即可。
    @Bean
    public PersistentTokenRepository persistentTokenRepository(){
        JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
        jdbcTokenRepository.setDataSource(dataSource);
        return jdbcTokenRepository;
    }
    // 自定义 PersistentTokenBasedRememberMeServices，允许通过 request.getParameter 获取令牌
    @Bean
    public MyPersistentRememberMeServices myPersistentRememberMeServices() throws Exception {
        return new MyPersistentRememberMeServices("key",userDetailsServiceBean(),persistentTokenRepository());
    }
    // 自定义 TokenBasedRememberMeServices，允许通过 request.getParameter 获取令牌
    @Bean
    public MyBaseRememberMeServices myBaseRememberMeServices() throws Exception {
        return new MyBaseRememberMeServices("key",userDetailsServiceBean());
    }

    /**
     * 在持久化令牌中，新增了两个经过 MD5 散列函数计算的校验参数，一个是 series，另一个是 token。
     * 其中，series 只有当用户在使用用户名/密码登录时，才会生成或者更新，而 token 只要有新的会话，就会重新生成，
     *
     * 这样当用户登录成功后，remember-me 令牌被人劫持了，劫持人并进行访问了，这个令牌就会改变，
     * 这时用户关闭浏览器重新打开建立会话时，使用 remember-me 进行登录就会发现登录不了，因为令牌改变了导致比对失败，此时会从数据库删除该用户的令牌，必须重新登录才能访问，
     * 这样被劫持的令牌就没有用了，提高了系统的安全性
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().authenticated()
                .and().formLogin()
                // 开启持久化的记住我功能，
                .and().rememberMe()
                .rememberMeServices(myBaseRememberMeServices())
//                .rememberMeServices(myPersistentRememberMeServices())
                //设置持久化令牌的仓库，设置 tokenRepository 后会自动创建 PersistentTokenBasedRememberMeServices，不设置则创建 TokenBasedRememberMeServices
//                .tokenRepository(persistentTokenRepository())
                .and().csrf().disable();
    }
}
